﻿#ifndef _EASYTCPSERVER_H_
#define _EASYTCPSERVER_H_

#include <vector>
#include <atomic>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <map>
#include <thread>
#include <mutex>
#include <atomic>
#include <algorithm>
#include <memory>
#include <iostream>
#include "CELL.h"
#include "CELLTimestamp.hpp"
#include "CELLTask.hpp"
#include "INetEvent.h"
#include "CELLServer.hpp"
#include "CELLClient.hpp"
#include "CELLTimer.hpp"
#include "CELLConfig.hpp"
#include "CELLNetWork.hpp"

/*
	用于接收新的客户端口，并添加到消费者线程中的客户端缓冲队列，也进行着统计收发包数量的工作
*/
class EasyTcpServer : public INetEvent
{
public:
	EasyTcpServer()
	{
		_nMaxClient = CELLConfig::Instance().getNum("nMaxClient", 1000);
		_nSendBuffSize = CELLConfig::Instance().getNum("nSendBuffSize", 20480);
		_nRecvBuffSize = CELLConfig::Instance().getNum("nRecvBuffSize", 10240);
	}

	virtual ~EasyTcpServer()
	{
		Close();
	}

	// 初始化socket
	SOCKET InitSocket()
	{
		CELLNetWork::Init();
		if (_sock != INVALID_SOCKET)
		{
			CELLLog_Warning("close old connect _sock : %d", _sock);
			Close();
		}

		_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (_sock == SOCKET_ERROR)
		{
			CELLLog_pError("create _sock error");
		}
		else
		{
			CELLNetWork::make_reuseaddr(_sock);
			CELLLog_Info("create _sock : %d successful", _sock);
		}
		return _sock;
	}

	// 绑定IP和端口
	int Bind(const char *ip, unsigned short port)
	{
		if (_sock == INVALID_SOCKET)
		{
			InitSocket();
		}
		struct sockaddr_in sin;
		sin.sin_family = AF_INET;
		sin.sin_port = htons(port);
		if (ip)
		{
			sin.sin_addr.s_addr = inet_addr(ip);
		}
		else
		{
			sin.sin_addr.s_addr = htonl(INADDR_ANY);
		}

		int ret = ::bind(_sock, (struct sockaddr*) & sin, sizeof(sin));
		if (ret == SOCKET_ERROR)
		{
			CELLLog_pError("bind sock : %d error ", (int)_sock);
		}
		CELLLog_Info("bind sock : %d success ", (int)_sock);
		return ret;
	}

	// 监听端口
	int Listen(int backlog)
	{
		if (_sock == INVALID_SOCKET)
		{
			return -1;
		}
		int ret = listen(_sock, backlog);
		if (ret == SOCKET_ERROR)
		{
			CELLLog_pError("listen sock : %d error", (int)_sock);
		}
		CELLLog_Info("listen sock : %d success", (int)_sock);
		return ret;
	}

	// 关闭socket
	void Close()
	{
		CELLLog_Info("EasyTcpServer Close ===== begin");
		_thread.Close();
		if (_sock != INVALID_SOCKET)
		{
			for (auto s : _cellServers)
			{
				delete s;
			}
			_cellServers.clear();

			CELLNetWork::destorySocket(_sock);
			_sock = INVALID_SOCKET;
		}
		CELLLog_Info("EasyTcpServer Close ===== end");
	}

	// 创建CELLServer
	template<typename ServerT>
	void Start(int nCELLServer);

	// 接收客户端连接
	SOCKET Accept()
	{
		if (_sock == SOCKET_ERROR)
		{
			return -1;
		}
		struct sockaddr_in client_addr;
		socklen_t client_len = sizeof(client_addr);
		SOCKET cSock = INVALID_SOCKET;
		cSock = accept(_sock, (struct sockaddr*) & client_addr, &client_len);
		if (cSock == INVALID_SOCKET)
		{
			CELLLog_pError("EasyTcpServer accept error on line %d", __LINE__);
		}
		else
		{
			if (_clientCount < _nMaxClient)
			{
				// 此处可以做登陆校验
				//CELLLog::Info("New Client IP: %s, PORT: %hu, sock: %d", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), (int)cSock);
				// 将新客户端分配给队列最小的一个服务线程去处理
				AddClientToCELLServer(new CELLClient(cSock, _nSendBuffSize, _nRecvBuffSize));
			}
			else
			{
				// 连接上限，可以给他发送一些消息或者做别的业务
				// 只要不是恶意攻击
				CELLNetWork::destorySocket(cSock);
				CELLLog_Warning("Accept to nMaxClient");
			}
		}
		return cSock;
	}

	void AddClientToCELLServer(CELLClient* pClient)
	{
		auto minServer = *(_cellServers.begin());
		for (std::vector<CELLServer*>::iterator it = _cellServers.begin(); it != _cellServers.end(); ++it)
		{
			// 选择一个存放客户端最少的CELLServer放进去
			if ((*it)->GetClientCount() < (*it)->GetClientCount())
			{
				minServer = *it;
			}
		}
		minServer->AddClient(pClient);
	}

	// 客户端加入事件，由主线程一个线程调用
	virtual void OnNetJoin(CELLClient* pClient)
	{
		++_clientCount;
		// CELLLog_Info("client<%d> join", pClient->sockfd());
	}

	// 客户端断开连接事件，由CELLServer多个线程调用 不安全
	virtual void OnNetLeave(CELLClient* pClient)
	{
		--_clientCount;
		// CELLLog_Info("client<%d> leave", pClient->sockfd());
	}

	// 客户端消息事件，由CELLServer多个线程调用 不安全
	virtual void OnNetMsg(CELLServer* pCELLServer, CELLClient* pCLient, DataHeader* header)
	{
		++_recvMsg;
	}

	virtual void OnNetRecv(CELLClient* pCLient)
	{
		++_recvCount;
	}

	// 注册时间事件，单位(秒)
	unsigned int add_time_event(CELLTimer::time_callback cb, void *ctx, unsigned int after_sec, int repeat = 1)
	{
		return _time_events.register_time_event(cb, ctx, after_sec, repeat);
	}

	// 更新时间事件，单位(秒)
	void mod_time_event(unsigned int timerid, unsigned int after_sec, int repeat = 1)
	{
		_time_events.update_time_event(timerid, after_sec, repeat);
	}

	// 删除时间事件，单位(秒)
	void del_time_event(unsigned int timerid)
	{
		_time_events.unregister_time_event(timerid);
	}

protected:
	// 定时事件处理
	CELLTimer _time_events;
	// 处理网络消息
	virtual void OnRun(CELLThread* pthread) = 0;

	// 处理消息，响应网络消息
	void time4msg()
	{
		auto t1 = _tTime.getElapsedSecond();
		if (t1 >= 1.0)
		{
			CELLLog_Info("thread_count<%d>, time<%lf>, sock<%d>, clients<%d>, recv<%d>, _msg<%d>", (int)_cellServers.size(), t1, _sock, (int)_clientCount, (int)(_recvCount), (int)(_recvMsg));
			_recvCount = 0;
			_recvMsg = 0;
			_tTime.update();
		}
	}

	SOCKET sockfd()
	{
		return _sock;
	}

	// recv函数计数
	std::atomic<int> _recvCount{ 0 };
	// 客户端连接数
	std::atomic<int> _clientCount{ 0 };
	// 客户端断开连接数
	std::atomic<int> _leaveCount{ 0 };
	// 收到消息计数
	std::atomic<int> _recvMsg{ 0 };
	// 接收新客户端时判断是否到了连接上限
	int _nMaxClient;
	// 创建新的CELLClient时候指定接收缓冲区和发送给缓冲区的大小
	int _nSendBuffSize;
	int _nRecvBuffSize;
private:
	//描述本类线程对象信息
	CELLThread _thread;
	// 存储着的多个CELLServer对象，用于给对象中的客户端缓冲队列中添加新加入的客户端
	// 每一个CELLServer都会创建一个线程用对象的OnRun作为线程入口函数
	std::vector<CELLServer*> _cellServers;
	// 每秒消息计数
	CELLTimestamp _tTime;
	// 监听套接字
	SOCKET _sock = INVALID_SOCKET;
};

// 创建CELLServer
template<typename ServerT>
void EasyTcpServer::Start(int nCELLServer)
{
	for (int i = 0; i < nCELLServer; ++i)
	{
		ServerT* ser = new ServerT();
		ser->SetId(i + 1);
		_cellServers.push_back(ser);
		// 设置事件对象，用于调用本对象的onNetLeave，OnNetMsg等事件处理函数
		ser->SetEventObj(this);
		// 在这个方法中会创建一个线程开启任务
		ser->Start();
	}
	// 让本任务也作为一个线程启动
	_thread.Start(
		// onCreate
		nullptr,
		// onRun
		[this](CELLThread* pthread) {
			OnRun(pthread);
		}
	);
}

#endif // #ifndef _EASYTCPSERVER_H_
